/******************************************************************************中
 * @brief: qcom data define
 * @Author: aple
 * @Date: 2022-07-28 08:03:56
 * @LastEditors: aple
 * @LastEditTime: 2022-12-10 09:10:09
 * @Verision: 1.0.0.0
 ******************************************************************************/

#ifndef _QC_COM_MOUDLE_
#define _QC_COM_MOUDLE_

#include "mytype.h"
#include "dat_quene.h"

#ifdef __cplusplus
extern "C"
{
#endif

/*****************************************************************************
 * @brief  CONFIG
 *****************************************************************************/
#define QC_BUFF_SIZE 256

/*****************************************************************************
 * @brief  工作模式
 *****************************************************************************/
#define QC_MODE_SLAVE 0
#define QC_MODE_MASTER 1

/*****************************************************************************
 * @brief   QC ID 配置
 *****************************************************************************/
#define QC_SLAVE_ID_MSK 0x0FU // 0号地址为广播地址，响应但不返回数据
#define QC_ZONE_POS 4U
#define QC_ZONE_MSK 0xF0U

	/*************************************************************************
	 * @brief   硬件状态
	 *************************************************************************/
	typedef enum
	{
		QC_RX_IDLE = 0,		 // 空闲
		QC_RX_RCV = 1,		 // 活动计时状态
		QC_RX_END = 2,		 // 任务处理状态，接收完成
		QC_RX_TOUT = 3,		 // 任务处理状态，等待超时
		QC_RX_WAIT = 4,		 // 活动计时状态，主机发完进入等待
		QC_RX_NACK_WAIT = 5, // 活动计时状态，主机发完进入无响应等待
		QC_RX_NACK_END = 6,	 // 任务处理状态，主机NACK结束
	} qc_rx_type;

	typedef enum
	{
		QC_TX_IDLE = 0, // 空闲
		QC_TX_BUSY = 1, // 发送状态
	} qc_tx_type;

	/*************************************************************************
	 * @brief   QC 从机回调
	 *************************************************************************/
	typedef const struct
	{
		void (*dat_set)(uu16 addr, uu16 dat); // addr 高8位为page，允许单指令跨页操作
		uu16 (*dat_get)(uu16 addr);			  // addr 高8位为page，允许单指令跨页操作
		uu16 *pdat_set;						  // set 内存映射区域内存地址
		cuu16 *pdat_get;					  // get 内存映射区域内存地址
		uu16 datset_mem_size;				  // set 内存映射区域，单位 u16，设为0无映射区。映射地址范围0~size-1。
		uu16 datget_mem_size;				  // get 内存映射区域，单位 u16，设为0无映射区。映射地址范围0~size-1。
	} qc_com_slave_type;

	/*************************************************************************
	 * @brief   MODBUS 从机回调
	 *************************************************************************/
	typedef const struct
	{
		void (*hold_dat_set)(uu16 addr, uu16 dat);	// 设置HOLD区数据,  06H, 10H, 17H
		uu16 (*hold_dat_get)(uu16 addr);			// 获取HOLD区数据,  03H, 17H
		uu16 (*input_dat_get)(uu16 addr);			// 获取INPUT区数据, 04H,
		void (*coil_dat_set)(uu16 Bindex, uu8 dat); // 设置线圈区数据,  05H, 0FH
		uu8 (*coil_dat_get)(uu16 Bindex);			// 获取线圈区数据,  01H
		uu8 (*disc_dat_get)(uu16 Bindex);			// 获取离散区数据,  02H
		uu16 *pdat_hold;							// HOLD区内存映射地址, 03H, 06H, 10H, 17H
		uu16 dat_hold_mem_size;						// HOLD区内存映射数量，设为0表示没有映射区
		const uu16 *pdat_input;						// INPUT区内存映射地址, 04H
		uu16 dat_input_mem_size;					// INPUT区内存映射数量，设为0表示没有映射区
		uu08 *pdat_coil;							// 线圈区内存映射地址, 01H, 05H, 0FH
		uu16 dat_coil_mem_size;						// 线圈区内存映射数量，设为0表示没有映射区
		const uu08 *pdat_disc;						// 离散区内存映射地址, 02H
		uu16 dat_disc_mem_size;						// 离散区内存映射数量，设为0表示没有映射区
	} qc_mb_slave_type;

	/*************************************************************************
	 * @brief   QC回调函数命名推荐
	 *************************************************************************/
	void qc_hold_dat_set(uu16 addr, uu16 dat);
	uu16 qc_hold_dat_get(uu16 addr);
	uu16 qc_input_dat_get(uu16 addr);
	void qc_coil_dat_set(uu16 Bindex, uu8 dat);
	uu8 qc_coil_dat_get(uu16 Bindex);
	uu8 qc_disc_dat_get(uu16 Bindex);
	extern uu16 qc_dat_hold[];
	extern uu16 qc_dat_input[];
	extern uu08 qc_dat_coil[];
	extern uu08 qc_dat_disc[];

	extern uu16 qc_dat_r[];
	extern uu16 qc_dat_w[];

/*****************************************************************************
 * @brief   MODBUS 指令区域及写指令分发方式
 *****************************************************************************/
#define QC_MB_ATTR_HOLD_SW (1) // REG单写（HOLDING 区）
#define QC_MB_ATTR_HOLD_MW (2) // REG多写（HOLDING 区）
#define QC_MB_ATTR_HOLD_RW (3) // REG读写（HOLDING 区）
#define QC_MB_ATTR_INPUT (4)   // 04指令区（INPUT 区）
#define QC_MB_ATTR_COIL_SW (5) // IO单写（COILS 区）
#define QC_MB_ATTR_COIL_MW (6) // IO多写（COILS 区）
#define QC_MB_ATTR_DISC (7)	   // 离散区（DISCRETE 区）

/*****************************************************************************
 * @brief   VGUS
 *****************************************************************************/
#define QC_GUS_ATTR_PV (1)
#define QC_GUS_ATTR_REG (2)
#define QC_GUS_ATTR_LINE (3)
#define QC_GUS_ATTR_AUTO (4)

	/*************************************************************************
	 * @brief   cmd struct
	 *************************************************************************/
	typedef struct
	{
		uu8 id;		 // 0 代表广播
		uu8 wn, rn;	 // 0 - 120
		uu8 attr;	 // 命令特性，禁止为0。
		uu16 wa, ra; // wa 高8位指示页面
		uu16 *rdat;
		uu16 *wdat;
		void (*callback)(uu8 site, uu8 err);
	} qc_cmd_type;

	/*************************************************************************
	 * @brief   API
	 *************************************************************************/
	typedef struct
	{
		void (*obj_init)(qc_cmd_type *clist, uu8 cnum, uu8 stcnum);
		void (*cmd_clr)(void);
		uint8_t (*dyna_cmd_req)(qc_cmd_type *cmd);
		uint8_t (*stc_cmd_req)(uint8_t cmd_site, qc_cmd_type *cmd);
		uint8_t (*stc_cmd_del)(uint8_t cmd_site);
		uint8_t (*cmd_ready_num)(void);
		uint8_t (*cmd_idle_num)(void);
	} qcom_api_type;

	/*************************************************************************
	 * @brief   qc object
	 *************************************************************************/
	typedef struct
	{
		qc_rx_type rx_st;			  // 收发状态
		qc_tx_type tx_st;			  // 发送状态
		uu8 mode;					  // 【Y】主从模式
		uu8 err_hal;				  //  hal 错误
		uu8 et_sv;					  // 【Y】主命令间隔
		uu8 slave_id;				  // 【Y】从机地址
		uu8 rid;					  // 从机接收到的ID
		uu8 tim_sv, tout_sv;		  // 【Y】断言时间, QC_RX_RCV状态计时，超时判断QC_RX_TERR。接收字符清除计数。
		uu8 tim_cnt, tout_cnt;		  // 超时时间, QC_RX_WAIT状态计时，超时判断QC_RX_TOUT。
		uu8 nack_tim_sv;			  // 【Y】主模式0号地址非应答，延时时间为 tim_sv + nack_tim_sv
		uu8 nack_tim_cnt;			  // nack 状态计时
		uu8 tx_cnt, tx_size;		  // 发送计数
		uu8 rx_cnt;					  // 接收计数
		uu16 idle_tim_cnt;			  // 串口空闲时间
		uu16 act_tim_cnt;			  // 串口活动时间
		uu16 success_cnt, fail_cnt;	  // 执行结果计数
		uu16 success_keep, fail_keep; // 执行结果连续计数
		uu16 sucess_none_time_cnt;	  // 通信成功后空闲时间
		const uu8 *aux_dat;			  // 【Y】叠加数据，0表示无叠加。使用该选项时，通信双方的数据表应相同。QCOM和MODBUS可用

		struct
		{
			com_quene_type dcmd_quene;
			qc_cmd_type *cmd_list;
			uu8 st_cmd_empty;
			uu8 stc_cmd_site;
			uu8 stc_cmd_size;
			uu8 cmd_exe_site;
			uu8 cmd_size;
			uu8 err_cnt, err_sv;
			uu8 cmd_split_cnt; // 内部管理
			uu8 fun_code;	   // 功能码
			qc_cmd_type cmd_now;
		} master;								  // 【Y】
		void const *slave;						  // 【Y】
												  //
		void (*os_event_send)(uu16 event);		  // 【Y】
		void (*dat_send)(void);					  // 【Y】
		void (*proc_callback)(uu8 site, uu8 err); // 【Y】对于从机，site 固定为0
		const qcom_api_type *fun;

		uu8 buff[QC_BUFF_SIZE];
	} qc_obj_type;

/*****************************************************************************
 * @brief   slave 默认指向内部定义接口，接口为弱定义，内容如下
 *****************************************************************************
 * OBJ_WEAK void qc_hold_dat_set(uu16 addr, uu16 dat){};
 * OBJ_WEAK uu16 qc_hold_dat_get(uu16 addr) { return 0; }
 * OBJ_WEAK uu16 qc_input_dat_get(uu16 addr) { return 0; }
 * OBJ_WEAK void qc_coil_dat_set(uu16 Bindex, uu8 dat){};
 * OBJ_WEAK uu8 qc_coil_dat_get(uu16 Bindex) { return 0; }
 * OBJ_WEAK uu8 qc_disc_dat_get(uu16 Bindex) { return 0; }
 * OBJ_WEAK qc_mb_slave_type qc_mb_slave = {
 * 	qc_hold_dat_set,
 * 	qc_hold_dat_get,
 * 	qc_input_dat_get,
 * 	qc_coil_dat_set,
 * 	qc_coil_dat_get,
 * 	qc_disc_dat_get,
 * };
 *****************************************************************************/

/*****************************************************************************
 * @brief   QC ERROR DEFINE
 *****************************************************************************/
#define QC_ERR_NONE 0
#define QC_ERR_RN 1
#define QC_ERR_WN 2
#define QC_ERR_RXCNT 3

#define QC_ERR_CRC 0x11
#define QC_ERR_RID 0x12
#define QC_ERR_TOUT 0x13

	/*************************************************************************
	 * @brief   QC API
	 *************************************************************************/
	void mqc_poll(qc_obj_type *obj);
	void sqc_poll(qc_obj_type *obj);
	void qc_poll(qc_obj_type *obj);

	/*************************************************************************
	 * @brief  MODBUS API
	 *************************************************************************/
	uint8_t mqc_mb_coils_read(qc_obj_type *obj);
	uint8_t mqc_mb_discrete_read(qc_obj_type *obj);
	uint8_t mqc_mb_holding_read(qc_obj_type *obj);
	uint8_t mqc_mb_input_read(qc_obj_type *obj);
	uint8_t mqc_mb_coils_write(qc_obj_type *obj, uint16_t reg_addr, uint16_t wdat);
	uint8_t mqc_mb_holding_write(qc_obj_type *obj, uint16_t reg_addr, uint16_t wdat);
	uint8_t mqc_mb_multi_coils_write(qc_obj_type *obj);
	uint8_t mqc_mb_multi_holding_write(qc_obj_type *obj);
	uint8_t mqc_mb_holding_rw(qc_obj_type *obj);

	void sqc_mb_poll(qc_obj_type *obj);
	void mqc_mb_poll(qc_obj_type *obj);
	void qc_mb_poll(qc_obj_type *obj);

/*****************************************************************************
 * @brief  VGUS API
 *****************************************************************************/
#define VGUS_RGE_LED 0x01
#define VGUS_RGE_BUZZER 0x02
#define VGUS_RGE_PAGE 0x03
#define VGUS_RGE_RUN_TIME 0x0C
#define VGUS_RGE_RTC_ADJ 0x1F
#define VGUS_RGE_RTC_NOW 0x20
#define VGUS_REG_MUSIC_PLAY 0x50
#define VGUS_REG_MUSIC_NUM 0x51
#define VGUS_REG_VOICE_ADJ 0x53
#define VGUS_REG_VOICE_VAL 0x54
#define VGUS_REG_AVI 0x60
	void qc_vgus_poll(qc_obj_type *obj);
	void qc_vgus_page_set(qc_obj_type *obj, uint16_t *page, void (*callback)(uint8_t, uint8_t));
	void qc_vgus_pv_read(qc_obj_type *obj, uint16_t *dat, uint16_t addr, uint16_t len, void (*callback)(uint8_t, uint8_t));
	void qc_vgus_pv_write(qc_obj_type *obj, uint16_t *dat, uint16_t addr, uint16_t len, void (*callback)(uint8_t, uint8_t));

	/*************************************************************************
	 * @brief  DGUS API
	 *************************************************************************/
	void qc_dgusii_poll(qc_obj_type *obj);
	void qc_dgus_poll(qc_obj_type *obj);

	/*************************************************************************
	 * @brief   OBJ & CMD
	 *************************************************************************/
	void qc_obj_init(qc_obj_type *obj, qc_cmd_type *clist, uu8 cnum, uu8 stcnum);
	void mqc_cmd_next(qc_obj_type *obj);
	void mqc_cmd_clr(qc_obj_type *obj);
	uint8_t mqc_dyna_cmd_req(qc_obj_type *obj, qc_cmd_type *cmd);
	uint8_t mqc_stc_cmd_req(qc_obj_type *obj, uint8_t cmd_site, qc_cmd_type *cmd);
	uint8_t mqc_stc_cmd_del(qc_obj_type *obj, uint8_t cmd_site);
	uint8_t mqc_cmd_ready_num(qc_obj_type *obj);
	uint8_t mqc_cmd_idle_num(qc_obj_type *obj);
	void qc_exe_success(qc_obj_type *obj);
	void qc_exe_fail(qc_obj_type *obj);

#define qcom_api_make(name, qobj)                                         \
	static void name##_obj_init(qc_cmd_type *clist, uu8 cnum, uu8 stcnum) \
	{                                                                     \
		qc_obj_init(&(qobj), clist, cnum, stcnum);                        \
	}                                                                     \
	static void name##_cmd_clr(void)                                      \
	{                                                                     \
		mqc_cmd_clr(&(qobj));                                             \
	}                                                                     \
	static uint8_t name##_dyna_cmd_req(qc_cmd_type *cmd)                  \
	{                                                                     \
		return mqc_dyna_cmd_req(&(qobj), cmd);                            \
	}                                                                     \
	static uint8_t name##_stc_cmd_req(uint8_t cmd_site, qc_cmd_type *cmd) \
	{                                                                     \
		return mqc_stc_cmd_req(&(qobj), cmd_site, cmd);                   \
	}                                                                     \
	static uint8_t name##_stc_cmd_del(uint8_t cmd_site)                   \
	{                                                                     \
		return mqc_stc_cmd_del(&(qobj), cmd_site);                        \
	}                                                                     \
	static uint8_t name##_cmd_ready_num(void)                             \
	{                                                                     \
		return mqc_cmd_ready_num(&(qobj));                                \
	}                                                                     \
	static uint8_t name##_cmd_idle_num(void)                              \
	{                                                                     \
		return mqc_cmd_idle_num(&(qobj));                                 \
	}                                                                     \
	static const qcom_api_type name##_api = {                             \
		name##_obj_init,                                                  \
		name##_cmd_clr,                                                   \
		name##_dyna_cmd_req,                                              \
		name##_stc_cmd_req,                                               \
		name##_stc_cmd_del,                                               \
		name##_cmd_ready_num,                                             \
		name##_cmd_idle_num}
#define qcom_api_ref(name) extern const qcom_api_type name##_api
#define qcom_api_name(name) &name##_api
#define qcom_api_link(name, qobj) qobj.fun = &name##_api

	/*****************************************************************************
	 * @brief   hal api
	 *****************************************************************************/
	// hal api
	void qc_data_rcv(qc_obj_type *obj, uu8 dat);
	void qc_data08_rcv(qc_obj_type *obj, volatile uu8 *rx_reg, uu8 len);
	void qc_data16_rcv(qc_obj_type *obj, volatile uu16 *rx_reg, uu8 len);
	void qc_data32_rcv(qc_obj_type *obj, volatile uu32 *rx_reg, uu8 len);
	uint8_t qc_data_send(qc_obj_type *obj, void (*send)(uint8_t), uint8_t len);
	uint8_t qc_data08_send(qc_obj_type *obj, volatile uint8_t *tx_reg, uint8_t fifo_len);
	uint8_t qc_data16_send(qc_obj_type *obj, volatile uint16_t *tx_reg, uint8_t fifo_len);
	uint8_t qc_data32_send(qc_obj_type *obj, volatile uint32_t *tx_reg, uint8_t fifo_len);

	void qc_send_end(qc_obj_type *obj);

	void qc_timer_handle(qc_obj_type *obj);
	void qc_package_send(qc_obj_type *obj, uu8 len);

	/*****************************************************************************
	 * END
	 *****************************************************************************/

#ifdef __cplusplus
}
#endif

#endif
